﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Input;

namespace Foundation
{
    /// <summary>
    /// View Model Base.
    /// </summary>
    public abstract class ViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Property values
        /// </summary>
        private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected ViewModel()
        {
            CommandBindings = new CommandBindingCollection();
        }

        /// <summary>
        /// Gets or sets the command bindings.
        /// </summary>
        /// <value>
        /// The command bindings.
        /// </value>
        public CommandBindingCollection CommandBindings { get; set; }

        /// <summary>
        /// Binds the specified control.
        /// </summary>
        /// <param name="control">The control.</param>
        public void Bind(FrameworkElement control)
        {
            control.DataContext = this;
            control.CommandBindings.AddRange(CommandBindings);
        }

        /// <summary>
        /// Gets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <returns>Property value</returns>
        public T Get<T>(Expression<Func<T>> expression)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);
            object value;

            if (_values.TryGetValue(propertyName, out value))
                return (T) value;

            return default(T);
        }

        /// <summary>
        /// Sets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <param name="value">The value.</param>
        public void Set<T>(Expression<Func<T>> expression, T value)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);

            if (_values.ContainsKey(propertyName))
                _values[propertyName] = value;
            else _values.Add(propertyName, value);

            RaisePropertyChangedEvent(propertyName);
        }

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lambdaExpression">The lambda expression.</param>
        public void RaisePropertyChangedEvent<T>(Expression<Func<T>> lambdaExpression)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(lambdaExpression);
            RaisePropertyChangedEvent(propertyName);
        }

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void RaisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}